﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Net;
using System.Text.RegularExpressions;
using System.ComponentModel;
using System.Linq;

namespace Marmat.Common
{

    /// <summary>
    /// FTPClient类提供了一套完整的方法操作文件的方法。
    /// </summary>
    public class FtpClient : Marmat.Common.IFileService
    {

        /// <summary>
        /// 构造函数
        /// </summary>
        public FtpClient() : this(null, "anonymous", "@anonymous") { }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="host">FTP地址</param>
        /// <param name="userName">登录用户名</param>
        /// <param name="password">登录密码</param>
        public FtpClient(string host, string userName, string password) : this(host, userName, password, null) { }

        /// <summary>
        ///   构造函数
        ///  </summary>
        ///  <param name="host">FTP地址</param>
        ///  <param name="userName">登录用户名</param>
        ///  <param name="password">登录密码</param>
        ///  <param name="proxy">连接代理</param>
        public FtpClient(string host, string userName, string password, WebProxy proxy)
        {
            this.WebClient = new WebClient();
            this.Host = host;
            this.UserName = userName;
            this.Password = password;
            this.Proxy = proxy;
        }

        private WebClient _webClient;
        private WebClient WebClient
        {
            get => _webClient;
            set
            {
                if (_webClient != null)
                {
                    _webClient.DownloadFileCompleted -= client_DownloadFileCompleted;
                    _webClient.DownloadProgressChanged -= client_DownloadProgressChanged;
                    _webClient.UploadFileCompleted -= client_UploadFileCompleted;
                    _webClient.UploadProgressChanged -= client_UploadProgressChanged;
                }
                _webClient = value;
                if (_webClient != null)
                {
                    _webClient.DownloadFileCompleted += client_DownloadFileCompleted;
                    _webClient.DownloadProgressChanged += client_DownloadProgressChanged;
                    _webClient.UploadFileCompleted += client_UploadFileCompleted;
                    _webClient.UploadProgressChanged += client_UploadProgressChanged;
                }
            }
        }

        /// <summary>
        /// 是否需要删除临时文件
        /// </summary>
        private bool _isDeleteTempFile = false;

        /// <summary>
        /// 异步上传所临时生成的文件
        /// </summary>
        private string _UploadTempFile = "";

        /// <summary>
        /// 连接FTP服务器的代理服务
        /// </summary>
        private WebProxy Proxy { get; set; }

        private Uri get_uri(string path = "")
        {
            if (!string.IsNullOrEmpty(path) && !path.StartsWith("/"))
                path = $"/{path}";
            return new Uri($"ftp://{this.Host}:{this.Port}{path}");
        }

        /// <summary>
        ///  FTP服务器地址
        ///  </summary>
        public string Host { get; set; }

        /// <summary>
        /// 端口号
        /// </summary>
        [DefaultValue(21)]
        public int Port { get; set; } = 21;

        /// <summary>
        /// 当前工作目录
        /// </summary>
        public string RootDir { get; set; }

        /// <summary>
        /// FTP登录用户
        /// </summary>
        public string UserName { get; set; }

        /// <summary>
        /// FTP登录密码
        /// </summary>
        public string Password { get; set; }

        /// <summary>
        /// 传输过程使用的字符编码
        /// </summary>
        public Encoding Encoding { get; set; } = System.Text.Encoding.UTF8;

        /// <summary>
        /// 异步下载进度发生改变触发的事件
        /// </summary>
        public event DownloadProgressChangedEventHandler DownloadProgressChanged;

        public delegate void DownloadProgressChangedEventHandler(object sender, DownloadProgressChangedEventArgs e);

        /// <summary>
        /// 异步下载文件完成之后触发的事件
        /// </summary>
        public event DownloadDataCompletedEventHandler DownloadDataCompleted;

        public delegate void DownloadDataCompletedEventHandler(object sender, AsyncCompletedEventArgs e);

        /// <summary>
        /// 异步上传进度发生改变触发的事件
        /// </summary>
        public event UploadProgressChangedEventHandler UploadProgressChanged;

        public delegate void UploadProgressChangedEventHandler(object sender, UploadProgressChangedEventArgs e);

        /// <summary>
        /// 异步上传文件完成之后触发的事件
        /// </summary>
        public event UploadFileCompletedEventHandler UploadFileCompleted;

        public delegate void UploadFileCompletedEventHandler(object sender, UploadFileCompletedEventArgs e);

        /// <summary>
        /// 建立FTP链接,返回响应对象
        /// </summary>
        /// <param name="uri">FTP地址</param>
        /// <param name="mathod">获取或设置要发送到 FTP 服务器的命令。</param>
        private FtpWebRequest Open(Uri uri, string mathod)
        {
            var request = (FtpWebRequest)WebRequest.Create(uri);
            request.Method = mathod;
            request.UseBinary = true;
            request.Credentials = new NetworkCredential(this.UserName, this.Password);
            request.Proxy = this.Proxy;
            return request;
        }

        /// <summary>
        /// 从FTP服务器下载文件，指定本地路径和本地文件名
        /// </summary>
        /// <param name="remoteFileName">远程文件名</param>
        /// <param name="localFileName">保存本地的文件名</param>
        public virtual void Download(string remoteFileName, string localFileName)
        {
            if (File.Exists(localFileName))
                throw new Exception("当前路径下已经存在同名文件！");
            byte[] bt = Download(remoteFileName);
            if (bt != null)
            {
                FileStream stream = new FileStream(localFileName, FileMode.Create);
                stream.Write(bt, 0, bt.Length);
                stream.Flush();
                stream.Close();
            }
        }

        /// <summary>
        /// 从FTP服务器下载文件，返回文件二进制数据
        /// </summary>
        /// <param name="remoteFileName">远程文件名</param>
        public virtual byte[] Download(string remoteFileName)
        {
            // 由于带有路径，导致下载失败，暂时注释此检测机制。
            //if (!IsValidFileChars(remoteFileName))
            //    throw new Exception("非法文件名或目录名!");
            var request = Open(this.get_uri(remoteFileName), WebRequestMethods.Ftp.DownloadFile);
            var response = request.GetResponse();
            Stream Reader = response.GetResponseStream();
            MemoryStream stream = new MemoryStream(1024 * 500);
            byte[] buffer = new byte[1024];
            int TotalByteRead = 0;
            while (true)
            {
                int bytesRead = Reader.Read(buffer, 0, buffer.Length);
                TotalByteRead += bytesRead;
                if (bytesRead == 0)
                    break;
                stream.Write(buffer, 0, bytesRead);
            }
            if (stream.Length > 0)
                return stream.ToArray();
            else
                return null;
        }

        /// <summary>
        /// 从FTP服务器异步下载文件，指定本地完整路径文件名
        /// </summary>
        /// <param name="remoteFileName">远程文件名</param>
        /// <param name="localFullPath">本地完整路径文件名</param>
        public virtual void DownloadAsync(string remoteFileName, string localFullPath)
        {
            this.DownloadAsync(remoteFileName, localFullPath, null);
        }

        /// <summary>
        /// 从FTP服务器异步下载文件，指定本地完整路径文件名
        /// </summary>
        /// <param name="sourceFileName">远程文件名</param>
        /// <param name="destinationFileName">本地完整路径文件名</param>
        /// <param name="userToken">用户数据</param>
        public virtual void DownloadAsync(string sourceFileName, string destinationFileName, object userToken)
        {
            if (!IsValidFileChars(sourceFileName))
                throw new Exception("非法文件名或目录名!");
            if (File.Exists(destinationFileName))
                throw new Exception("当前路径下已经存在同名文件！");
            this.WebClient.Credentials = new NetworkCredential(this.UserName, this.Password);
            this.WebClient.Proxy = this.Proxy;
            Uri uri = this.get_uri(sourceFileName);
            this.WebClient.DownloadFileAsync(uri, destinationFileName, userToken);
        }

        /// <summary>
        /// 异步下载文件完成之后触发的事件
        /// </summary>
        /// <param name="sender">下载对象</param>
        /// <param name="e">数据信息对象</param>
        private void client_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {
            DownloadDataCompleted?.Invoke(sender, e);
        }

        /// <summary>
        /// 异步下载进度发生改变触发的事件
        /// </summary>
        /// <param name="sender">下载对象</param>
        /// <param name="e">进度信息对象</param>
        private void client_DownloadProgressChanged(object sender, System.Net.DownloadProgressChangedEventArgs e)
        {
            DownloadProgressChanged?.Invoke(sender, e);
        }

        /// <summary>
        /// 上传文件到FTP服务器
        /// </summary>
        /// <param name="localFileName">本地带有完整路径的文件名</param>
        public virtual bool Upload(string localFileName)
        {
            return Upload(localFileName, Path.GetFileName(localFileName), true);
        }

        /// <summary>
        /// 上传文件到FTP服务器
        /// </summary>
        /// <param name="localFileName">本地带有完整路径的文件</param>
        /// <param name="remoteFileName">要在FTP服务器上面保存文件名</param>
        public virtual bool Upload(string localFileName, string remoteFileName)
        {
            return this.Upload(localFileName, remoteFileName, true);
        }

        /// <summary>
        /// 上传文件到FTP服务器
        /// </summary>
        /// <param name="localFileName">本地带有完整路径的文件</param>
        /// <param name="overWriteRemoteFile">是否覆盖远程服务器上面同名的文件</param>
        public virtual bool Upload(string localFileName, bool overWriteRemoteFile)
        {
            return Upload(localFileName, Path.GetFileName(localFileName), overWriteRemoteFile);
        }

        /// <summary>
        /// 上传文件到FTP服务器
        /// </summary>
        /// <param name="localFileName">本地带有完整路径的文件名</param>
        /// <param name="remoteFileName">要在FTP服务器上面保存文件名</param>
        /// <param name="overWriteRemoteFile">是否覆盖远程服务器上面同名的文件</param>
        public virtual bool Upload(string localFileName, string remoteFileName, bool overWriteRemoteFile)
        {
            if (!IsValidFileChars(remoteFileName) || !IsValidFileChars(Path.GetFileName(localFileName)) || !IsValidPathChars(Path.GetDirectoryName(localFileName)))
                throw new Exception("非法文件名或目录名!");
            if (File.Exists(localFileName))
            {
                FileStream Stream = new FileStream(localFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                byte[] bt = new byte[Stream.Length - 1 + 1];
                Stream.Read(bt, 0, System.Convert.ToInt32(Stream.Length));
                // 注意，因为Int32的最大限制，最大上传文件只能是大约2G多一点
                Stream.Close();
                return this.Upload(bt, remoteFileName, overWriteRemoteFile);
            }
            else
                throw new Exception("本地文件不存在!");
        }

        /// <summary>
        /// 上传文件到FTP服务器
        /// </summary>
        /// <param name="fileData">上传的二进制数据</param>
        /// <param name="remoteFileName">要在FTP服务器上面保存文件名</param>
        public virtual bool Upload(byte[] fileData, string remoteFileName)
        {
            if (!IsValidFileChars(remoteFileName))
                throw new Exception("非法文件名或目录名!");
            return Upload(fileData, remoteFileName, false);
        }

        /// <summary>
        /// 上传文件到FTP服务器
        /// </summary>
        /// <param name="fileData">文件二进制内容</param>
        /// <param name="remoteFileName">要在FTP服务器上面保存文件名</param>
        /// <param name="overWriteRemoteFile">是否覆盖远程服务器上面同名的文件</param>
        public virtual bool Upload(byte[] fileData, string remoteFileName, bool overWriteRemoteFile)
        {
            if (!IsValidFileChars(remoteFileName))
                throw new Exception("非法文件名！");
            if (!overWriteRemoteFile && FileExist(remoteFileName))
                throw new Exception("FTP服务上面已经存在同名文件！");
            var request = Open(this.get_uri(remoteFileName), WebRequestMethods.Ftp.UploadFile);
            Stream requestStream = request.GetRequestStream();
            MemoryStream mem = new MemoryStream(fileData);

            byte[] buffer = new byte[1024];
            int bytesRead = 0;
            int TotalRead = 0;
            while (true)
            {
                bytesRead = mem.Read(buffer, 0, buffer.Length);
                if (bytesRead == 0)
                    break;
                TotalRead += bytesRead;
                requestStream.Write(buffer, 0, bytesRead);
            }
            requestStream.Close();
            request.GetResponse();
            mem.Close();
            mem.Dispose();
            fileData = null;
            return true;
        }

        /// <summary>
        /// 异步上传文件到FTP服务器
        /// </summary>
        /// <param name="localFileName">本地带有完整路径的文件名</param>
        public virtual void UploadAsync(string localFileName)
        {
            UploadAsync(localFileName, Path.GetFileName(localFileName), false, null);
        }

        /// <summary>
        /// 异步上传文件到FTP服务器
        /// </summary>
        /// <param name="localFileName">本地带有完整路径的文件</param>
        /// <param name="overWriteRemoteFile">是否覆盖远程服务器上面同名的文件</param>
        public virtual void UploadAsync(string localFileName, bool overWriteRemoteFile)
        {
            UploadAsync(localFileName, Path.GetFileName(localFileName), overWriteRemoteFile, null);
        }

        /// <summary>
        /// 异步上传文件到FTP服务器
        /// </summary>
        /// <param name="localFileName">本地带有完整路径的文件</param>
        /// <param name="remoteFileName">要在FTP服务器上面保存文件名</param>
        public virtual void UploadAsync(string localFileName, string remoteFileName)
        {
            UploadAsync(localFileName, remoteFileName, true, null);
        }

        public virtual void UploadAsync(string localFileName, string remoteFileName, object userToken)
        {
            UploadAsync(localFileName, remoteFileName, true, userToken);
        }

        /// <summary>
        /// 异步上传文件到FTP服务器
        /// </summary>
        /// <param name="localFileName">本地带有完整路径的文件名</param>
        /// <param name="remoteFileName">要在FTP服务器上面保存文件名</param>
        /// <param name="overWriteRemoteFile">是否覆盖远程服务器上面同名的文件</param>
        /// <param name="userToken">用户数据</param>
        public virtual void UploadAsync(string localFileName, string remoteFileName, bool overWriteRemoteFile, object userToken)
        {
            if (!IsValidFileChars(remoteFileName) || !IsValidFileChars(Path.GetFileName(localFileName)) || !IsValidPathChars(Path.GetDirectoryName(localFileName)))
                throw new Exception("非法文件名或目录名!");
            if (!overWriteRemoteFile && FileExist(remoteFileName))
                throw new Exception("FTP服务上面已经存在同名文件！");
            if (File.Exists(localFileName))
            {
                this.WebClient.Credentials = new System.Net.NetworkCredential(this.UserName, this.Password);
                this.WebClient.Proxy = this.Proxy;
                Uri Uri = new Uri(this.get_uri(remoteFileName).ToString());
                if (userToken == null)
                    this.WebClient.UploadFileAsync(Uri, localFileName);
                else
                    this.WebClient.UploadFileAsync(Uri, "POST", localFileName, userToken);
            }
            else
                throw new Exception("本地文件不存在!");
        }


        /// <summary>
        /// 异步上传文件到FTP服务器
        /// </summary>
        /// <param name="FileBytes">上传的二进制数据</param>
        /// <param name="RemoteFileName">要在FTP服务器上面保存文件名</param>
        public virtual void UploadAsync(byte[] FileBytes, string RemoteFileName)
        {
            if (!IsValidFileChars(RemoteFileName))
                throw new Exception("非法文件名或目录名!");
            UploadAsync(FileBytes, RemoteFileName, false);
        }

        /// <summary>
        /// 异步上传文件到FTP服务器
        /// </summary>
        /// <param name="fileData">文件二进制内容</param>
        /// <param name="remoteFileName">要在FTP服务器上面保存文件名</param>
        /// <param name="overWriteRemoteFile">是否覆盖远程服务器上面同名的文件</param>
        public virtual void UploadAsync(byte[] fileData, string remoteFileName, bool overWriteRemoteFile)
        {
            if (!IsValidFileChars(remoteFileName))
                throw new Exception("非法文件名！");
            if (!overWriteRemoteFile && FileExist(remoteFileName))
                throw new Exception("FTP服务上面已经存在同名文件！");
            string TempPath = System.Environment.GetFolderPath(Environment.SpecialFolder.Templates);
            if (!TempPath.EndsWith(@"\"))
                TempPath += @"\";
            string TempFile = TempPath + Path.GetRandomFileName();
            TempFile = Path.ChangeExtension(TempFile, Path.GetExtension(remoteFileName));
            FileStream Stream = new FileStream(TempFile, FileMode.CreateNew, FileAccess.Write);
            Stream.Write(fileData, 0, fileData.Length);
            // 注意，因为Int32的最大限制，最大上传文件只能是大约2G多一点
            Stream.Flush();
            Stream.Close();
            Stream.Dispose();
            _isDeleteTempFile = true;
            _UploadTempFile = TempFile;
            fileData = null;
            UploadAsync(TempFile, remoteFileName, overWriteRemoteFile, null);
        }

        /// <summary>
        /// 异步上传文件完成之后触发的事件
        /// </summary>
        /// <param name="sender">下载对象</param>
        /// <param name="e">数据信息对象</param>
        private void client_UploadFileCompleted(object sender, System.Net.UploadFileCompletedEventArgs e)
        {
            if (_isDeleteTempFile)
            {
                if (File.Exists(_UploadTempFile))
                {
                    File.SetAttributes(_UploadTempFile, FileAttributes.Normal);
                    File.Delete(_UploadTempFile);
                }
                _isDeleteTempFile = false;
            }
            UploadFileCompleted?.Invoke(sender, e);
        }

        /// <summary>
        /// 异步上传进度发生改变触发的事件
        /// </summary>
        /// <param name="sender">下载对象</param>
        /// <param name="e">进度信息对象</param>
        private void client_UploadProgressChanged(object sender, System.Net.UploadProgressChangedEventArgs e)
        {
            UploadProgressChanged?.Invoke(sender, e);
        }

        /// <summary>
        /// 从FTP服务器上面删除一个文件
        /// </summary>
        /// <param name="RemoteFileName">远程文件名</param>
        public void Delete(string RemoteFileName)
        {
            if (!IsValidFileChars(RemoteFileName))
                throw new Exception("文件名非法！");
            Uri Uri = this.get_uri(RemoteFileName);
            var request = Open(Uri, WebRequestMethods.Ftp.DeleteFile);
            request.GetResponse();
        }

        public void DeleteAsync(string fileName)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        ///  列出FTP服务器上面当前目录的所有文件和目录
        ///  </summary>
        public FtpFileStruct[] ListFilesAndDirectories()
        {
            var request = Open(this.get_uri(), WebRequestMethods.Ftp.ListDirectoryDetails);
            var response = request.GetResponse();
            StreamReader stream = new StreamReader(response.GetResponseStream(), this.Encoding);
            string value = stream.ReadToEnd();
            FtpFileStruct[] list = GetList(value);
            return list;
        }

        /// <summary>
        /// 列出FTP服务器上面当前目录的所有文件
        /// </summary>
        public FtpFileStruct[] ListFiles()
        {
            FtpFileStruct[] listAll = ListFilesAndDirectories();
            List<FtpFileStruct> listFile = new List<FtpFileStruct>();
            foreach (FtpFileStruct file in listAll)
            {
                if (!file.IsDirectory)
                    listFile.Add(file);
            }
            return listFile.ToArray();
        }

        /// <summary>
        /// 列出FTP服务器上面当前目录的所有的目录
        /// </summary>
        public FtpFileStruct[] ListDirectories()
        {
            FtpFileStruct[] listAll = ListFilesAndDirectories();
            return listAll.ToList().Where(x => x.IsDirectory).ToArray();
        }

        /// <summary>
        /// 获得文件和目录列表
        /// </summary>
        /// <param name="datastring">FTP返回的列表字符信息</param>
        private FtpFileStruct[] GetList(string datastring)
        {
            List<FtpFileStruct> items = new List<FtpFileStruct>();
            string[] records = datastring.Split(new string[] { "\r\n" }, StringSplitOptions.None);
            FTPListStyle _directoryListStyle = GuessFileListStyle(records);

            DateTimeFormatInfo dateTimeFormat = new CultureInfo("en-US", false).DateTimeFormat;
            dateTimeFormat.ShortTimePattern = "t";
            foreach (string record in records)
            {
                if (_directoryListStyle != FTPListStyle.Unknown && record != "")
                {
                    FtpFileStruct item = new FtpFileStruct();
                    switch (_directoryListStyle)
                    {
                        case FTPListStyle.UnixStyle:
                            {
                                // 从Unix格式中返回文件信息
                                string processstr = record.Trim();
                                item = new FtpFileStruct();
                                item.Flags = processstr.Substring(0, 10);
                                item.IsDirectory = item.Flags[0] == 'd';
                                processstr = processstr.Substring(11).Trim();
                                _cutSubstringFromStringWithTrim(ref processstr, ' ', 0);
                                // 跳过一部分
                                item.Owner = _cutSubstringFromStringWithTrim(ref processstr, ' ', 0);
                                item.Group = _cutSubstringFromStringWithTrim(ref processstr, ' ', 0);
                                _cutSubstringFromStringWithTrim(ref processstr, ' ', 0);
                                // 跳过一部分
                                string yearOrTime = processstr.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)[2];
                                if (yearOrTime.IndexOf(":") >= 0)
                                    processstr = processstr.Replace(yearOrTime, DateTime.Now.Year.ToString());
                                item.CreateTime = DateTime.Parse(_cutSubstringFromStringWithTrim(ref processstr, ' ', 8));
                                item.Name = processstr;
                            }
                            break;
                        case FTPListStyle.WindowsStyle:
                            {
                                //从Windows格式中返回文件信息
                                item = new FtpFileStruct();
                                string processstr = record.Trim();
                                string dateStr = processstr.Substring(0, 8);
                                processstr = (processstr.Substring(8, processstr.Length - 8)).Trim();
                                string timeStr = processstr.Substring(0, 7);
                                processstr = (processstr.Substring(7, processstr.Length - 7)).Trim();
                                item.CreateTime = DateTime.Parse(dateStr + " " + timeStr, dateTimeFormat);
                                if (processstr.Substring(0, 5) == "<DIR>")
                                {
                                    item.IsDirectory = true;
                                    processstr = (processstr.Substring(5, processstr.Length - 5)).Trim();
                                }
                                else
                                {
                                    int IndexOf = processstr.IndexOf(" ");
                                    processstr = processstr.Substring(IndexOf + 1);
                                    item.IsDirectory = false;
                                }
                                item.Name = processstr;
                            }
                            break;
                    }
                    if (!string.IsNullOrEmpty(item.Name))
                        items.Add(item);
                }
            }
            return items.ToArray();
        }


        /// <summary>
        /// 判断文件列表的方式Window方式还是Unix方式
        /// </summary>
        /// <param name="recordList">文件信息列表</param>
        private FTPListStyle GuessFileListStyle(string[] recordList)
        {
            foreach (string s in recordList)
            {
                if (s.Length > 10 && Regex.IsMatch(s.Substring(0, 10), "(-|d)(-|r)(-|w)(-|x)(-|r)(-|w)(-|x)(-|r)(-|w)(-|x)"))
                    return FTPListStyle.UnixStyle;
                else if (s.Length > 8 && Regex.IsMatch(s.Substring(0, 8), "[0-9][0-9]-[0-9][0-9]-[0-9][0-9]"))
                    return FTPListStyle.WindowsStyle;
            }
            return FTPListStyle.Unknown;
        }

        /// <summary>
        /// 从Unix格式中返回文件信息
        /// </summary>
        /// <param name="Record">文件信息</param>
        private FtpFileStruct ParseFTPFileStructFromUnixStyleRecord(string Record)
        {
            FtpFileStruct f = new FtpFileStruct();
            string processstr = Record.Trim();
            f.Flags = processstr.Substring(0, 10);
            f.IsDirectory = (f.Flags[0] == 'd');
            processstr = (processstr.Substring(11)).Trim();
            _cutSubstringFromStringWithTrim(ref processstr, ' ', 0);
            // 跳过一部分
            f.Owner = _cutSubstringFromStringWithTrim(ref processstr, ' ', 0);
            f.Group = _cutSubstringFromStringWithTrim(ref processstr, ' ', 0);
            _cutSubstringFromStringWithTrim(ref processstr, ' ', 0);
            // 跳过一部分
            string yearOrTime = processstr.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)[2];
            if (yearOrTime.IndexOf(":") >= 0)
                // time
                processstr = processstr.Replace(yearOrTime, DateTime.Now.Year.ToString());
            f.CreateTime = DateTime.Parse(_cutSubstringFromStringWithTrim(ref processstr, ' ', 8));
            f.Name = processstr;
            // 最后就是名称
            return f;
        }

        /// <summary>
        /// 按照一定的规则进行字符串截取
        /// </summary>
        /// <param name="s">截取的字符串</param>
        /// <param name="c">查找的字符</param>
        /// <param name="startIndex">查找的位置</param>
        private string _cutSubstringFromStringWithTrim(ref string s, char c, int startIndex)
        {
            int pos1 = s.IndexOf(c, startIndex);
            string retString = s.Substring(0, pos1);
            s = (s.Substring(pos1)).Trim();
            return retString;
        }

        /// <summary>
        /// 判断当前目录下指定的子目录是否存在
        /// </summary>
        /// <param name="RemoteDirectoryName">指定的目录名</param>
        public bool DirectoryExist(string RemoteDirectoryName)
        {
            if (!IsValidPathChars(RemoteDirectoryName))
                throw new Exception("目录名非法！");
            FtpFileStruct[] listDir = ListDirectories();
            foreach (FtpFileStruct dir in listDir)
            {
                if (dir.Name == RemoteDirectoryName)
                    return true;
            }
            return false;
        }

        /// <summary>
        /// 判断一个远程文件是否存在服务器当前目录下面
        /// </summary>
        /// <param name="remoteFileName">远程文件名</param>
        public bool FileExist(string remoteFileName)
        {
            if (!IsValidFileChars(remoteFileName))
                throw new Exception("文件名非法！");
            FtpFileStruct[] listFile = ListFiles();
            foreach (FtpFileStruct file in listFile)
            {
                if (file.Name == remoteFileName)
                    return true;
            }
            return false;
        }

        /// <summary>
        /// 更改一个文件的名称或一个目录的名称
        /// </summary>
        /// <param name="remoteFileName">原始文件或目录名称</param>
        /// <param name="newFileName">新的文件或目录的名称</param>
        [Obsolete("测试未通过")]
        public void ReName(string remoteFileName, string newFileName)
        {
            if (!IsValidFileChars(remoteFileName) || !IsValidFileChars(newFileName))
                throw new Exception("文件名非法！");
            else if (remoteFileName == newFileName)
                return;
            var request = Open(this.get_uri(remoteFileName), WebRequestMethods.Ftp.Rename);
            request.RenameTo = newFileName;
            request.GetResponse();
        }

        /// <summary>
        /// 把当前目录下面的一个文件拷贝到服务器上面另外的目录中，注意，拷贝文件之后，当前工作目录还是文件原来所在的目录
        /// </summary>
        /// <param name="RemoteFile">当前目录下的文件名</param>
        /// <param name="DirectoryName">新目录名称。
        /// 说明：如果新目录是当前目录的子目录，则直接指定子目录。如: SubDirectory1/SubDirectory2 ；
        /// 如果新目录不是当前目录的子目录，则必须从根目录一级一级的指定。如： ./NewDirectory/SubDirectory1/SubDirectory2
        /// </param>
        /// <returns></returns>
        public bool CopyFileToAnotherDirectory(string RemoteFile, string DirectoryName)
        {
            string rootDir = this.RootDir;
            byte[] bt = Download(RemoteFile);
            GotoDirectory(DirectoryName);
            bool success = Upload(bt, RemoteFile, false);
            this.RootDir = rootDir;
            return success;
        }

        /// <summary>
        /// 把当前目录下面的一个文件移动到服务器上面另外的目录中，注意，移动文件之后，当前工作目录还是文件原来所在的目录
        /// </summary>
        /// <param name="RemoteFile">当前目录下的文件名</param>
        /// <param name="DirectoryName">新目录名称。
        /// 说明：如果新目录是当前目录的子目录，则直接指定子目录。如: SubDirectory1/SubDirectory2 ；
        /// 如果新目录不是当前目录的子目录，则必须从根目录一级一级的指定。如： ./NewDirectory/SubDirectory1/SubDirectory2
        /// </param>
        /// <returns></returns>
        public void MoveFileToAnotherDirectory(string RemoteFile, string DirectoryName)
        {
            string dir = this.RootDir;
            if (DirectoryName == "")
                return;
            if (!DirectoryName.StartsWith("/"))
                DirectoryName = "/" + DirectoryName;
            if (!DirectoryName.EndsWith("/"))
                DirectoryName += "/";
            ReName(RemoteFile, DirectoryName + RemoteFile);
            this.RootDir = dir;
        }

        /// <summary>
        /// 在FTP服务器上当前工作目录建立一个子目录
        /// </summary>
        /// <param name="directoryName">子目录名称</param>
        public bool MakeDirectory(string directoryName)
        {
            if (!IsValidPathChars(directoryName))
                throw new Exception("目录名非法！");
            var request = Open(this.get_uri(directoryName), WebRequestMethods.Ftp.MakeDirectory);
            request.GetResponse();
            return true;
        }

        /// <summary>
        /// 从当前工作目录中删除一个子目录
        /// </summary>
        /// <param name="directoryName">子目录名称</param>
        public bool RemoveDirectory(string directoryName)
        {
            if (!IsValidPathChars(directoryName))
                throw new Exception("目录名非法！");
            var request = Open(this.get_uri(directoryName), WebRequestMethods.Ftp.RemoveDirectory);
            request.GetResponse();
            return true;
        }

        /// <summary>
        /// 判断目录名中字符是否合法
        /// </summary>
        /// <param name="DirectoryName">目录名称</param>
        private bool IsValidPathChars(string DirectoryName)
        {
            char[] invalidPathChars = Path.GetInvalidPathChars();
            char[] DirChar = DirectoryName.ToCharArray();
            foreach (char C in DirChar)
            {
                if (Array.BinarySearch(invalidPathChars, C) >= 0)
                    return false;
            }
            return true;
        }

        /// <summary>
        /// 判断文件名中字符是否合法
        /// </summary>
        /// <param name="FileName">文件名称</param>
        private bool IsValidFileChars(string FileName)
        {
            char[] invalidFileChars = Path.GetInvalidFileNameChars();
            char[] NameChar = FileName.ToCharArray();
            foreach (char C in NameChar)
            {
                if (Array.BinarySearch(invalidFileChars, C) >= 0)
                    return false;
            }
            return true;
        }

        /// <summary>
        /// 进入一个目录
        /// </summary>
        /// <param name="directoryName">
        /// 新目录的名字。 
        /// 说明：如果新目录是当前目录的子目录，则直接指定子目录。如: SubDirectory1/SubDirectory2 ； 
        /// 如果新目录不是当前目录的子目录，则必须从根目录一级一级的指定。如： ./NewDirectory/SubDirectory1/SubDirectory2
        /// </param>
        public bool GotoDirectory(string directoryName)
        {
            string rootDir = this.RootDir;
            directoryName = directoryName.Replace(@"\", "/");
            string[] DirectoryNames = directoryName.Split(new char[] { '/' });
            if (DirectoryNames[0] == ".")
            {
                this.RootDir = "/";
                if (DirectoryNames.Length == 1)
                    return true;
                Array.Clear(DirectoryNames, 0, 1);
            }
            bool Success = false;
            foreach (string dir in DirectoryNames)
            {
                if (dir != null)
                {
                    Success = EnterOneSubDirectory(dir);
                    if (!Success)
                    {
                        this.RootDir = rootDir;
                        return false;
                    }
                }
            }
            return Success;
        }

        /// <summary>
        /// 从当前工作目录进入一个子目录
        /// </summary>
        /// <param name="DirectoryName">子目录名称</param>
        private bool EnterOneSubDirectory(string DirectoryName)
        {
            if (DirectoryName.IndexOf("/") >= 0 || !IsValidPathChars(DirectoryName))
                throw new Exception("目录名非法!");
            if (DirectoryName.Length > 0 && DirectoryExist(DirectoryName))
            {
                if (!DirectoryName.EndsWith("/"))
                    DirectoryName += "/";
                RootDir += DirectoryName;
                return true;
            }
            else
                return false;
        }

        /// <summary>
        /// 从当前工作目录往上一级目录
        /// </summary>
        public bool ComeoutDirectory()
        {
            if (RootDir == "/")
                throw new Exception("当前目录已经是根目录！");
            char[] sp = new char[1] { '/' };
            string[] strDir = RootDir.Split(sp, StringSplitOptions.RemoveEmptyEntries);
            if (strDir.Length == 1)
                RootDir = "/";
            else
                RootDir = String.Join("/", strDir, 0, strDir.Length - 1);
            return true;
        }

        /// <summary>
        /// 表示要用于检索 FTP 服务器上的文件大小的 FTP SIZE 协议方法。
        /// </summary>
        /// <param name="FileName"></param>
        /// <returns></returns>
        /// <remarks></remarks>
        internal long GetFileSize(string FileName)
        {
            FtpWebRequest ftp;
            string Uri = "ftp://" + this.Host + ":" + 21 + "/" + FileName;
            ftp = (FtpWebRequest)FtpWebRequest.Create(new Uri(Uri));
            ftp.Credentials = new NetworkCredential(UserName, Password);
            ftp.Method = WebRequestMethods.Ftp.GetFileSize;
            FtpWebResponse ftpresponse = (FtpWebResponse)ftp.GetResponse();
            long size = ftpresponse.ContentLength;
            return size;
        }

        /// <summary>
        /// 生成特定文件的uri地址。
        /// </summary>
        /// <param name="FileName"></param>
        /// <returns></returns>
        public Uri GeneratePresignedUri(string FileName)
        {
            string Result = string.Format("ftp://{0}:{1}@{2}:{3}", this.UserName, this.Password, this.Host, this.Port);
            if (FileName.StartsWith("/") == false)
                FileName = "/" + FileName;
            return new Uri(Result + FileName);
        }

        public Uri GeneratePresignedUri(string FileName, object Obj)
        {
            return this.GeneratePresignedUri(FileName);
        }

    }
}
